function table.map(array, func)
  local new_array = {}
  for i,v in ipairs(array) do
    new_array[i] = func(v)
  end
  return new_array
end

function table.deep_copy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[table.deep_copy(orig_key)] = table.deep_copy(orig_value)
        end
        setmetatable(copy, table.deep_copy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end


function table.compare(array, compare_func)
  local highest = nil

  for i, v in ipairs(array) do
    if (highest == nil or compare_func(v, highest)) then
      highest = v
    end
  end

  return highest
end

function table.find_index(match, array)
  for i, v in ipairs(array) do
    if v == match then return i end
  end

  return -1
end

function table.find_all(array, func)
  results = {}

  for i, v in ipairs(array) do
    if func(v) then
      results[#results+1] = v
    end
  end

  return results
end


function table.insert_table(array, insert_array, index)
  new_table = table.deep_copy(array)

  index = index == nil and #array or index
  for i, v in ipairs(insert_array) do
    table.insert(new_table, index+i, v)
  end

  return new_table
end

function comma_value(amount)
  local formatted = amount
  while true do
    formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
    if (k==0) then
      break
    end
  end
  return formatted
end

function round(val, decimal)
  if (decimal) then
    return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
  else
    return math.floor(val+0.5)
  end
end

function format_num(amount, decimal, prefix, neg_prefix)
  local str_amount,  formatted, famount, remain

  decimal = decimal or 2  -- default 2 decimal places
  neg_prefix = neg_prefix or "-" -- default negative sign

  famount = math.abs(round(amount,decimal))
  famount = math.floor(famount)

  remain = round(math.abs(amount) - famount, decimal)

        -- comma to separate the thousands
  formatted = comma_value(famount)

        -- attach the decimal portion
  if (decimal > 0) then
    remain = string.sub(tostring(remain),3)
    formatted = formatted .. "." .. remain ..
                string.rep("0", decimal - string.len(remain))
  end

        -- attach prefix string e.g '$'
  formatted = (prefix or "") .. formatted

        -- if value is negative then format accordingly
  if (amount<0) then
    if (neg_prefix=="()") then
      formatted = "("..formatted ..")"
    else
      formatted = neg_prefix .. formatted
    end
  end

  return formatted
end

function iif(condition, if_val, else_val)
  if condition then
    return if_val
  else
    return else_val
  end
end
